/*  f_grammar.cpp

Functional Grammar class for determining whether
Lexemes match the grammar.

Nicholas Green
Recognizer
*/

#include "f_grammar.h"
#include "types.h"
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <assert.h>
#include <string.h>

F_Grammar::F_Grammar(FILE* file)
{
  m_recognizer = new Recognizer(file);

  //Put in order by use.
  ops[0] = OP_ASN;
  ops[1] = AOP_ADD;
  ops[2] = AOP_SUB;
  ops[3] = AOP_MUL;
  ops[4] = AOP_DIV;
  ops[5] = AOP_MOD;
  ops[6] = COP_EQUAL;
  ops[7] = COP_NEQUAL;
  ops[8] = COP_GREATER;
  ops[9] = COP_LESS;
  ops[10] = COP_GRTEQU;
  ops[11] = COP_LESEQU;
  ops[12] = LOP_NOT;
  ops[13] = LOP_AND;
  ops[14] = LOP_OR;
  ops[15] = BOP_NOT;
  ops[16] = BOP_AND;
  ops[17] = BOP_OR;
  ops[18] = BOP_XOR;    
  ops[19] = BOP_LSFT;
  ops[20] = BOP_RSFT;

  m_recognizer->advance();
}

F_Grammar::~F_Grammar()
{
  if (m_recognizer != NULL)
    delete(m_recognizer);
}

bool F_Grammar::displayPending()
{
  return m_recognizer->check(DISPLAY);
}

Lexeme* F_Grammar::display()
{
  Lexeme* id = m_recognizer->match(DISPLAY);
  Lexeme* val = expression();
  m_recognizer->match(SEMI);
  Lexeme* r = e.cons(id, e.cons(val, NULL));
  r->tVal = DISPLAY;
  return r;
}

bool F_Grammar::displayNPending()
{
  return m_recognizer->check(DISPLAYN);
}

Lexeme* F_Grammar::displayN()
{
  Lexeme* id = m_recognizer->match(DISPLAYN);
  Lexeme* val = expression();
  m_recognizer->match(SEMI);
  Lexeme* r = e.cons(id, e.cons(val, NULL));
  r->tVal = DISPLAYN;
  return r;
}

bool F_Grammar::funcDefPending()
{
  return m_recognizer->check(DEFINE);
}

Lexeme* F_Grammar::funcDef()
{
  /*
  funcDef : DEFINE VARIABLE block
  */
  m_recognizer->match(DEFINE);
  Lexeme* id = m_recognizer->match(ID);
  m_recognizer->match(OPAREN);
  Lexeme* params = optParamList();
  params->sVal = "PARAMS";
  m_recognizer->match(CPAREN);
  Lexeme* actions = block();
  actions->sVal = "BLOCK";

  Lexeme* s = e.cons(id, e.cons(params, e.cons(actions,NULL)));
  s->tVal = FUNCDEF;
  return s;
}

Lexeme* F_Grammar::optParamList()
{
  Lexeme* p = NULL;
  if(primaryPending())
  {
    p = primary();
    return e.cons(p, optParamList());
  }
  
  else
    return NULL;
}

bool F_Grammar::optParamListPending()
{
  return primaryPending();
}
bool F_Grammar::primaryPending()
{
  return m_recognizer->check(INTEGER)
    || varExpressionPending()
    || m_recognizer->check(OPAREN);
}
Lexeme* F_Grammar::primary()
{
  if (m_recognizer->check(INTEGER))
  {
    return m_recognizer->match(INTEGER);
  }
  else if (varExpressionPending())
  {
    return varExpression();
  }
  else if (m_recognizer->check(OPAREN))
  {
    m_recognizer->match(OPAREN);
    return expression();
    m_recognizer->match(CPAREN);
  }
  else
  {
    throw exit_exception(1,m_recognizer->getCurrentLexeme()->getTVal(),
      m_recognizer->getCurrentLexeme()->getSVal().c_str(),OPAREN,
      m_recognizer->getCurrentLexeme()->getLine());
  }
}

bool F_Grammar::expressionPending()
{
  return primaryPending();
}

Lexeme* F_Grammar::expression()
{
  Lexeme* p = primary();
  if (operatorPending())
  {
    Lexeme* o=foperator();
    Lexeme* pri = p;
    Lexeme* ex = expression();
    /*o->leftPtr = p;
    o->rightPtr = expression();*/
    Lexeme* r = e.cons(p,
      e.cons(o,
      e.cons(ex,NULL)));
    r->tVal = EXPR;
    return r;
  }
  return p;
}

bool F_Grammar::varExpressionPending()
{
  return m_recognizer->check(ID);
}

bool F_Grammar::varDefPending()
{
  return m_recognizer->check(VARDEF);
}

Lexeme* F_Grammar::varDef()
{
  Lexeme* n = m_recognizer->match(VARDEF);
  Lexeme* id = m_recognizer->match(ID);
  Lexeme* value = NULL;
  if(m_recognizer->check(OP_ASN))
  {
    m_recognizer->match(OP_ASN);
    value = expression(); //primary();
  }
  m_recognizer->match(SEMI);
  Lexeme* c = e.cons(id,value);
  c->tVal = VARDEF;
  return c;
}

//function call
Lexeme* F_Grammar::varExpression()
{
  Lexeme* n = m_recognizer->match(ID);
  if (m_recognizer->check(OPAREN))
  {
    m_recognizer->match(OPAREN);
    Lexeme* o = optExpressionList();
    m_recognizer->match(CPAREN);
    Lexeme* call = e.cons(n, e.cons(o, NULL));
    call->tVal = FUNC_CALL;
    return call;
  }
  return n;
}

bool F_Grammar::optExpressionListPending()
{
  return true;
}
//TODO: check if correct
Lexeme* F_Grammar::optExpressionList()
{

  if (expressionPending())
  {
    Lexeme* n = expression();
    return e.cons(n, optExpressionList());
  }
  return NULL;
}

bool F_Grammar::operatorPending()
{
  for (int i = 0; i < 21; i++)
  {
    if (m_recognizer->check(ops[i].c_str()))
      return true;
  }
  return false;

}

Lexeme* F_Grammar::foperator()
{
  int i = 0;
  bool done = false;
  while( (i < 21) && (done == false))
  {
    if(m_recognizer->check(ops[i].c_str()))
    {
      return m_recognizer->match(ops[i].c_str());
      done = true;
    }
    ++i;
  }

  try
  {
    if (done == false)
      throw exit_exception(1,m_recognizer->getCurrentLexeme()->getTVal(),
      m_recognizer->getCurrentLexeme()->getSVal().c_str(),"Operator", m_recognizer->getCurrentLexeme()->getLine());
  }
  catch(exit_exception& e)
  {
    exit(e.m_code);
  }
  //should never reach
  Lexeme* n = new Lexeme(NULL,m_recognizer->getCurrentLexeme()->getLine());
  return n;
}

bool F_Grammar::ifStatementPending()
{
  return m_recognizer->check(COND_IF);
}

Lexeme* F_Grammar::ifStatement()
{
  Lexeme* n = m_recognizer->match(COND_IF);
  m_recognizer->match(OPAREN);
  Lexeme* x = expression();
  m_recognizer->match(CPAREN);
  Lexeme* b = block();
  b->sVal = "block";

  Lexeme* r = e.cons(x, e.cons(b, NULL));
  r->tVal = COND_IF;
  r->sVal = "IF";

  return r; //e.cons(n, e.cons(x, e.cons(b, NULL)));
}

bool F_Grammar::blockPending()
{
  return m_recognizer->check(OBRACE);
}

Lexeme* F_Grammar::block()
{

  m_recognizer->match(OBRACE);
  Lexeme* p = e.cons( statementList(), NULL);
  m_recognizer->match(CBRACE);
  p->sVal = "BLOCK";
  return p;
}

bool F_Grammar::statementListPending()
{
  return statementPending();
}

Lexeme* F_Grammar::statementList()
{
  Lexeme* s = statement();
  if (statementListPending())
  {
    Lexeme* r = e.cons( s, e.cons(statementList(),NULL));
    r->tVal = SSQ;
    return r;
  }
  return s;
}

bool F_Grammar::statementPending()
{
  return expressionPending()
    || ifStatementPending()
    || switchStatementPending()
    || m_recognizer->check(TYPE_INT)
    || funcDefPending()
    || whileStatementPending()
    || varDefPending()
    || displayPending()
    || displayNPending();
}

Lexeme* F_Grammar::statement()
{
  /*
  statement : expression SEMI 
  | ifStatement
  | INT VARIABLE optInit SEMI
  | funcDef
  */
  if(expressionPending())
  {
    Lexeme* x = expression();
    m_recognizer->match(SEMI);
    return x;
  }
  else if(ifStatementPending())
    return ifStatement();
  else if(funcDefPending())
    return funcDef();
  else if(switchStatementPending())
    return switchStatement();
  else if(whileStatementPending())
    return whileStatement();
  else if(varDefPending())
    return varDef();
  else if(displayPending())
    return display();
  else if(displayNPending())
    return displayN();
  else
  {
    Lexeme* n = m_recognizer->match(ID);
    Lexeme* o = optInit();
    m_recognizer->match(SEMI);
    Lexeme* r = e.cons(n, e.cons(o, NULL));
    r->tVal = o->tVal;
    return r;
  }
}

Lexeme* F_Grammar::optElse()
{    
  /*
  optElse : ELSE block 
  | *empty* 
  */
  if(m_recognizer->check(COND_ELSE))
  {
    Lexeme* l = m_recognizer->match(COND_ELSE);
    Lexeme* b = block();
    return e.cons(l, e.cons(b, NULL));
  }
  else
    return NULL;
}

Lexeme* F_Grammar::optInit()
{
  /*  optInit : ASSIGN expression 
  | *empty*
  */ 
  if(m_recognizer->check(OP_ASN))
  {
    Lexeme* l = m_recognizer->match(OP_ASN);
    Lexeme* x = expression();
    Lexeme* r = e.cons(l, e.cons(x, NULL));
    r->tVal = OP_ASN;
    return r;
  }
  else
    return NULL;
}

bool F_Grammar::whileStatementPending()
{
  return m_recognizer->check(COND_WHILE);
}

Lexeme* F_Grammar::whileStatement()
{
  /*
  whileStatement : WHILE OPAREN expression CPAREN block
  */
  Lexeme* w = m_recognizer->match(COND_WHILE);
  m_recognizer->match(OPAREN);
  Lexeme* x = expression();
  m_recognizer->match(CPAREN);
  Lexeme* b = block();
  b->sVal = "BLOCK";
  m_recognizer->match(SEMI);
  Lexeme* r = e.cons(w, e.cons(x, e.cons(b, NULL)));
  r->tVal = COND_WHILE;
  return r;
}

bool F_Grammar::switchStatementPending()
{
  return m_recognizer->check(COND_SWITCH);
}

Lexeme* F_Grammar::switchStatement() 
{
  Lexeme* n = m_recognizer->match(COND_SWITCH);
  m_recognizer->match(OPAREN);
  Lexeme* x = expression();
  m_recognizer->match(CPAREN);
  m_recognizer->match(OBRACE);
  Lexeme* c = new Lexeme();
  if(optCaseListPending())
    c = optCaseList();
  m_recognizer->match(CBRACE);
  return e.cons(n, e.cons(x, e.cons(c, NULL)));
}

bool F_Grammar::optCaseListPending()
{
  return m_recognizer->check(COND_CASE);
}

Lexeme* F_Grammar::optCaseList()
{
  if(m_recognizer->check(COND_CASE))
  {
    m_recognizer->match(COND_CASE);
    expression();
    m_recognizer->match(COLON);
    if(optMultCaseListPending())
      optMultCaseList();
    block();
    m_recognizer->match(BREAK);
    m_recognizer->match(SEMI);
  }
  if(optCaseListPending())
    optCaseList();
  else if(m_recognizer->check(COND_DEFAULT))
  {
    m_recognizer->match(COND_DEFAULT);
    m_recognizer->match(COLON);
    block();
  }
  return NULL;
}

bool F_Grammar::optMultCaseListPending()
{
  return m_recognizer->check(COND_CASE);
}

Lexeme* F_Grammar::optMultCaseList()
{
  m_recognizer->match(COND_CASE);
  expression();
  m_recognizer->match(COLON);
  if(optMultCaseListPending())
    optMultCaseList();
  return NULL;
}

void F_Grammar::pp(Lexeme* pt)
{

  if(strcmp(pt->tVal,INTEGER) == 0)
    printf("%d",pt->iVal);
  else if(strcmp(pt->tVal,STRING) == 0)
    printf("\"%s\"",pt->sVal.c_str());
  else if(strcmp(pt->tVal,VARDEF) == 0)
    printVarDef(pt);
  else if(strcmp(pt->tVal, ID) == 0)
    printf("%s ",pt->sVal.c_str());
  else if(strcmp(pt->tVal, OP_ASN) == 0)
    printf(" = ");
  else if(strcmp(pt->tVal,FUNCDEF) == 0)
    printFuncDef(pt);
  else if(strcmp(pt->tVal, FUNC_CALL) == 0)
    printFuncCall(pt);
  else if(strcmp(pt->tVal, EXPR) == 0)
    ppList(pt, " ");
  else if(strcmp(pt->tVal, SSQ) == 0)
    ppList(pt, "\n");
  else if(strcmp(pt->tVal, COND_WHILE) == 0)
    printWhile(pt);
  else if(strcmp(pt->tVal, COND_IF) == 0)
    printCond(pt);
  else if(strcmp(pt->tVal, COP_EQUAL) == 0)
    printf(" == ");
  else if(strcmp(pt->tVal, AOP_ADD) == 0)
    printf(" + ");
  else if(strcmp(pt->tVal, AOP_SUB) == 0)
    printf(" - ");
  else if(strcmp(pt->tVal, AOP_MUL) == 0)
    printf(" * ");
  else if(strcmp(pt->tVal, AOP_DIV) == 0)
    printf(" / ");
  else if(strcmp(pt->tVal, AOP_MOD) == 0)
    printf(" %% ");
  else if(strcmp(pt->tVal, LOP_NOT) == 0)
    printf(" ! ");
  else if(strcmp(pt->tVal, LOP_AND) == 0)
    printf(" && ");
  else if(strcmp(pt->tVal, LOP_OR) == 0)
    printf(" || ");
  else if(strcmp(pt->tVal, BOP_NOT) == 0)
    printf(" ~ ");
  else if(strcmp(pt->tVal, BOP_AND) == 0)
    printf(" & ");
  else if(strcmp(pt->tVal, BOP_OR) == 0)
    printf(" | ");
  else if(strcmp(pt->tVal, BOP_LSFT) == 0)
    printf(" << ");
  else if(strcmp(pt->tVal, BOP_RSFT) == 0)
    printf(" >> ");
  else if(strcmp(pt->tVal, COP_NEQUAL) == 0)
    printf(" != ");
  else if(strcmp(pt->tVal, COP_GREATER) == 0)
    printf(" > ");
  else if(strcmp(pt->tVal, COP_LESS) == 0)
    printf(" < ");
  else if(strcmp(pt->tVal, COP_GRTEQU) == 0)
    printf(" >= ");
  else if(strcmp(pt->tVal, COP_LESEQU) == 0)
    printf(" <= ");  
}
void F_Grammar::ppList(Lexeme* pt, const char* separator)
{ 
  if(pt != NULL)
  {
    if(pt->leftPtr != NULL)
    {
      pp(pt->leftPtr); 
      printf("%s",separator);
      ppList(pt->rightPtr, separator);
    }
  }
}

void F_Grammar::printFuncDef(Lexeme* pt)
{
  printf("define ");
  pp(e.car(pt));
  printf("(");
  ppList(e.cadr(pt), " ");
  printf(")\n");
  printf("{\n");
  Lexeme* q = e.cdr(pt);
  Lexeme* r = e.cdr(q);
  Lexeme* s = e.car(r);
  ppList(s, "\n");
  printf("\n}");
}

void F_Grammar::printCond(Lexeme* pt)
{
  printf("if ");
  printf("(");
  ppList(e.car(pt), " ");
  printf(")\n");
  printf("{\n");
  Lexeme* s = e.cadr(pt);
  ppList(s, "\n");
  printf("\n}");
}

void F_Grammar::printFuncCall(Lexeme* pt)
{
  printf("%s ", e.car(pt)->sVal.c_str());
  printf("(");
  ppList(e.cadr(pt), " ");
  printf(")");
  printf(";");
}

void F_Grammar::printVarDef(Lexeme* pt)
{
  printf("var ");
  printf("%s ", e.car(pt)->getSVal().c_str());
  printf("= ");
  pp(e.cdr(pt));
  printf(";");
}

void F_Grammar::printWhile(Lexeme* pt)
{
  printf("while (");
  pp(e.cadr(pt));
  printf(")\n{\n");
  pp(e.car(e.caddr(pt)));
  printf("\n};");
}
